/*-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License.You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
	
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.
	
The Original Code is Fever RS ( ActionScript 3 ) Framework code.
	
The Initial Developer of the Original Code is Romain Ecarnot.
Portions created by Initial Developer are Copyright (C) 2006
the Initial Developer. All Rights Reserved.
	
Contributor(s):
	
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-----------------------------------------------------------------------------*/

package fever.utils 
{

	/**
	 * Generate stream of pseudorandom numbers, chars or words.
	 * 
	 * @author Romain Ecarnot
	 */
	final public class Random 
	{

		//--------------------------------------------------------------------
		// Constants
		//--------------------------------------------------------------------
				
		/** Defines if char or word must be in upper case or not. */
		public static const UPPER_TYPE : String = "upper";

		/** Defines if char or worf must be in lower case or not. */
		public static const LOWER_TYPE : String = "lower";
		
		/** 
		 * Defines if char or word can contain upper and lower 
		 * case char.
		 */
		public static const MIXED_TYPE : String = "mixed";
		
		/**
		 * Default password length.
		 * 
		 * @see #nextPassword()
		 */
		public static const DEFAULT_PASSWORD_LENGTH : Number = 8;
		
		/** Special characters which can be used to genrate password. */
		public static const SPECIAL_CHARACTERS_TABLE : Array = 
			[ "+", "&", "\"", "'", "(", "[", "-", "|", "_", "^", ")", "]", 
			"?", "$", "?", "*", "?", "?", "?", "!", ":", "/", ";", ".", 
			",", "?","{", "}" ];
		
		
		//-------------------------------------------------------------------------
		// Public API
		//-------------------------------------------------------------------------
		
		/**
		 * Returns a new random boolean.
		 */
		public static function nextBoolean( ) : Boolean
		{
			return ( Math.random( ) < 0.5 );
		}

		/**
		 * Returns a new random number.
		 * 
		 * @param limit Limit of possible generated number
		 */
		public static function nextFloat( limit : int ) : Number
		{
			var max : int = ( limit >= 0 ) ? limit : int.MAX_VALUE;
			
			return Math.random( ) * max;
		}

		/**
		 * Return a new random <code>int</code>.
		 * 
		 * @param limit Limit of possible generated number
		 */
		public static function nextInt( limit : int ) : int
		{
			var result : Number = nextFloat( limit );
			return Math.round( result );
		}

		/**
		 * Returns a new random char.
		 * 
		 * @param readable Is generation still in human readable chars table
		 * @param type Possible values are :
		 * <ul>
		 *   <li>Random.UPPER_TYPE</li>
		 *   <li>Random.LOWER_TYPE</li>
		 *   <li>Random.MIXED_TYPE</li>
		 * </ul>
		 * 
		 * @see #UPPER_TYPE		 * @see #LOWER_TYPE		 * @see #MIXED_TYPE
		 */
		public static function nextChar( readable : Boolean, type : String ) : String
		{
			var human : Boolean = ( readable == false ) ? false : true;
			
			var min : Number = 97;  
			var max : Number = 122;
			
			if( !human ) 
			{
				min = 0;
				max = 255;
			}
			
			var result : String = String.fromCharCode( 
				MathUtil.random( min, max ) 
			);
			
			switch( type )
			{
				case UPPER_TYPE :
					return result.toUpperCase( );
				case MIXED_TYPE : 
					return ( nextBoolean( ) ) 
						? result 
						: result.toUpperCase( );
				default :
					return result;
			}
		}

		/**
		 * Returns a new random word.
		 * 
		 * @param limit Max chars in word ( must be > -1 and < 27 )
		 * @param type Possible values are :
		 * <ul>
		 *   <li>Random.UPPER_TYPE</li>
		 *   <li>Random.LOWER_TYPE</li>
		 *   <li>Random.MIXED_TYPE</li>
		 * </ul>
		 * 
		 * @see #UPPER_TYPE
		 * @see #LOWER_TYPE
		 * @see #MIXED_TYPE
		 */
		public static function nextWord( limit : int, type : String ) : String
		{
			var result : String = '';
			var l : int = 26;
			if( limit > -1 && limit < 27) l = limit;
			
			for( var i : int = 0; i < l ; i += 1 )
			{
				var char : String = nextChar( true, type );
				result += char;
			}
			return result;
		}

		/**
		 * Returns a new random generated password.
		 * 
		 * @param useNumber <code>true</code> to enable number in 
		 * string ( default is <code>true</code> )
		 * @param mixCap <code>true</code> to enable lower and uppercase 
		 * mix ( either just lowercase are used ) 
		 * ( default is <code>true</code> )
		 * @param useOther <code>true</code> to enable special character 
		 * use ( default is <code>false</code> )
		 * @param min ( optional )Minimum length for generated 
		 * password ( default is Random.DEFAULT_PASSWORD_LENGTH )
		 * @param max ( optional )Maximum length for generated password
		 * 
		 * @return String password
		 */
		public static function nextPassword( 
			useNumber : Boolean = true, 
			mixCap : Boolean = true, 
			useOther : Boolean = true, 
			min : Number = DEFAULT_PASSWORD_LENGTH, 
			max : Number = DEFAULT_PASSWORD_LENGTH 
			) : String
		{
			useNumber = ( useNumber == false ) ? false : true;
			mixCap = ( mixCap == false ) ? false : true;
			useOther = ( useOther == true ) ? true : false;
			
			if( isNaN( min ) || min <= 0 ) min = DEFAULT_PASSWORD_LENGTH;
			if( isNaN( max ) || max < min ) max = min;
			
			var template : Array = _createTemplate( 
				useNumber, mixCap, useOther 
			);
			
			var l : Number = MathUtil.random( min, max, true );
			var result : String = '';
			
			for ( var i : int = 0; i < l ; i += 1 )
			{
				result += ArrayUtil.getRandomSlot( template )( );
			}
			
			return result;
		}

		
		//--------------------------------------------------------------------
		// Private implementations
		//--------------------------------------------------------------------
		
		/** @private */
		public function Random( access : PrivateConstructorAccess ) 
		{
		}
		
		private static function _createTemplate( useNumber : Boolean, mixCap : Boolean, useOther : Boolean ) : Array
		{
			var pattern : Array = new Array( );
			
			if( useNumber ) pattern.push( _getNumber );
			
			if( mixCap ) pattern.push( _getMix );
			else pattern.push( _getLower );
			
			if( useOther ) pattern.push( _getCustom );
			
			return pattern;
		}
		
		private static function _getNumber() : String
		{
			return String( MathUtil.random( 0, 9, true ) );
		}
		
		private static function _getLower() : String
		{
			return nextChar( true, Random.LOWER_TYPE );	
		}
		
		private static function _getMix() : String
		{
			return nextChar( true, Random.MIXED_TYPE );
		}
		
		private static function _getCustom() : String
		{
			return String( 
				ArrayUtil.getRandomSlot( SPECIAL_CHARACTERS_TABLE ) 
			);	
		}
	}
}


internal class PrivateConstructorAccess 
{
}
